package com.example.springbootquartz.quartz;

import com.example.springbootquartz.constant.QuartzEnum;
import com.example.springbootquartz.entity.SysJob;
import lombok.SneakyThrows;
import org.quartz.*;
import org.springframework.util.ObjectUtils;

import java.util.Date;


/**
 * @author fulin
 * @since 2023/6/14 15:23
 * <p>
 * 此类作为操作job的工具类,这里的job与quartz中的 JobDetail一一对应 ,包括任务的增删改 {@link JobDetail}
 * </p>
 */
public class QuartzUtil {

    /**
     * 将新增的任务放入quartz调度中心中执行
     *
     * @param sysJob 具体任务信息
     * @throws Exception 异常
     */
    @SneakyThrows
    public static void insertOrUpdateJob(SysJob sysJob) {
        // 获取调度器 Scheduler
        Scheduler scheduler = SchedulerStatic.getScheduler();
        Long jobId = sysJob.getJobId();
        String jobGroup = sysJob.getJobGroup();

        // 构造一个job
        JobKey jobKey = JobKey.jobKey(jobId.toString(), jobGroup);
        JobDetail jobDetail = JobBuilder
                .newJob(QuartzJob.class)
                .withIdentity(jobKey)
                .build();

        // 构造cron调度器
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(sysJob.getCronExpression());
        getMisfirePolicy(sysJob, cronScheduleBuilder);

        // 构造触发器 trigger
        TriggerKey triggerKey = TriggerKey.triggerKey(jobId.toString(), jobGroup);
        CronTrigger trigger = TriggerBuilder
                .newTrigger()
                .withIdentity(triggerKey)
                .withSchedule(cronScheduleBuilder)
                .build();

        // 放入job信息,为后续执行改任务的具体方法做铺垫(需要反射调用), 在execute中获取并应用
        jobDetail.getJobDataMap().put(QuartzEnum.jobKey, sysJob);
        // 判断该任务是否存在,修改任务,先删除然后添加
        if (scheduler.checkExists(jobKey)) {
            // 防止创建时存在数据问题 先移除，然后在执行创建操作
            scheduler.deleteJob(jobKey);
        }
        // 判断任务是否过期
        CronExpression cron = new CronExpression(sysJob.getCronExpression());
        Date nextValidTimeAfter = cron.getNextValidTimeAfter(new Date(System.currentTimeMillis()));
        if (!ObjectUtils.isEmpty(nextValidTimeAfter)) {
            // 执行调度任务
            scheduler.scheduleJob(jobDetail, trigger);
        }
        // 暂停任务
        if (sysJob.getStatus().equals(QuartzEnum.PAUSE)) {
            scheduler.pauseJob(jobKey);
        }
    }


    /**
     * 根据计划执行错误策略,设置调度器
     *
     * @param sysJob              任务信息
     * @param cronScheduleBuilder cron调度器
     */
    private static void getMisfirePolicy(SysJob sysJob, CronScheduleBuilder cronScheduleBuilder) {
        switch (sysJob.getMisfirePolicy()) {
            case QuartzEnum.MISFIRE_DEFAULT:
                break;
            case QuartzEnum.MISFIRE_IGNORE_MISFIRES:
                cronScheduleBuilder.withMisfireHandlingInstructionIgnoreMisfires();
                break;
            case QuartzEnum.MISFIRE_FIRE_AND_PROCEED:
                cronScheduleBuilder.withMisfireHandlingInstructionFireAndProceed();
                break;
            case QuartzEnum.MISFIRE_DO_NOTHING:
                cronScheduleBuilder.withMisfireHandlingInstructionDoNothing();
                break;
            default:
                throw new RuntimeException("The task misfire policy '" + sysJob.getMisfirePolicy() + "' cannot be used in cron schedule tasks");
        }
    }

    /**
     * 删除任务
     *
     * @param sysJob 具体任务信息(获取jobkey)
     */
    @SneakyThrows
    public static void deleteJob(SysJob sysJob) {
        // 获取调度器 Scheduler
        Scheduler scheduler = SchedulerStatic.getScheduler();
        scheduler.deleteJob(JobKey.jobKey(sysJob.getJobId().toString(), sysJob.getJobGroup()));
    }
}
